#pragma once

#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
//#include <openssl/ssl.h>

#include <libge/libge.h>
#include "Player.h"
#include "slot.h"

class Client;
class ThreadListen;

class ClientConnect {
public:
	ClientConnect(Client* cli);
	~ClientConnect();
	bool isConnected();
	void Join(const char* _address, int _port);
	void Encrypt(u8* data, int size, u8** out, int *outlen);
	void Decrypt(u8* data, int size, u8** out, int *outlen);
	void ReadData(u8* ret, int size);
	char ReadByte();
	short ReadShort();
	int ReadInt();
	uint64_t ReadLong();
	float ReadFloat();
	double ReadDouble();
	int ReadString(char* out);
	const char* GetString();
	void ReadSlots(Slot* slots, int n);
	void ReadMetadata(Metadata* metas);
	int SendInit();
	int SendFinalize(int buf_id, bool force_send = false);
	void SendByte(int buf_id, char b);
	void SendShort(int buf_id, short s);
	void SendInt(int buf_id, int i);
	void SendLong(int buf_id, int64_t l);
	void SendFloat(int buf_id, float f);
	void SendDouble(int buf_id, double d);
	void SendString(int buf_id, const char* str);
	void WaitCommand(u8 id, u32 timeout);
	int SendClientStatuses();
	int SendHandshake(const char* player, const char* address, int port);
	int SendEncryptionKeyResponse(u8* key, int keylen, u8* token, int tokenlen);
	void EncryptionKeyRequest(u8** keyp, int* keylenp, u8** tokenp, int* tokenlenp);
	void GetKeepAlive();
	void GetLogin();
	void GetMessage();
	void TimeUpdate();
	void EntityEquipment();
	void SpawnPosition();
	void UpdateHealth();
	void SendPlayer(Player* p);
	void SendPlayerPosition(Player* p);
	void GetPlayerPositionLook();
	void SendPlayerPositionLook(Player* p);
	void GetHeldItemChange();
	void SpawnNamedEntity();
	void SpawnObjectVehicle();
	void SpawnMob();
	void SpawnPainting();
	void EntityVelocity();
	void DestroyEntity();
	void EntityRelativeMove();
	void EntityLook();
	void EntityLookandRelativeMove();
	void EntityTeleport();
	void EntityHeadLook();
	void EntityStatus();
	void EntityMetadata();
	void SetExperience();
	void ChunkData();
	void MultiBlockChange();
	void BlockChange();
	void BlockAction();
	void MapChunkBulk();
	void NamedSoundEffect();
	void ChangeGameState();
	void GetSlot();
	void GetWindowItems();
	void GetUpdateSign();
	void UpdateTileEntity();
	void PlayerListItem();
	void GetPlayerAbilities();
	void SendClientSettings();
	void GetPluginMessage();
	void ServerError();
	void setReceiving(bool recv);
	
private:
	void Connect();
	int Send(u8* buf, int buflen);
	int Receive(u8* pbuf, int max);
	u8* GetReadPtr(int size);
	u8* DecodeX509(u8* key, int keylen, u8* token, int tokenlen, u8** tokenout);
	void CiphersInit(u8* key, u8* iv);
	int CalculatePacketSize(u8* buf, int bufsize);
	int computeString(char* out, const char* in);
	Client* client;
	ge_Socket* socket;
	char* address;
	int port;
	bool connected;
	bool receiving;
	bool sending_data;
	bool sending[8];
	u8 recv_buf[65536+16];
	u32 recv_buf_size;
	u32 recv_buf_i;
	u8 send_buf[8][8192];
	u32 send_buf_i[8];
	int send_i;
	ThreadListen* threadListen;
	bool secureMode;
	int KEYLEN;
	int PRIVATE_KEYLEN;
	u8* private_key;
	EVP_CIPHER_CTX ctx_encrypt;
	EVP_CIPHER_CTX ctx_decrypt;
	u32 ctx_block_size;
	char string_read[8][2048];
	int string_read_i;
};
